/*
 * Copyright 2009 Armando Blancas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package tests.taskgraph.channels;

import java.io.EOFException;
import java.io.IOException;
import java.util.Arrays;

import junit.framework.Assert;
import junit.framework.JUnit4TestAdapter;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import taskgraph.channels.IntChannel;
import taskgraph.ports.IntInputPort;
import taskgraph.ports.IntOutputPort;

public class IntChannelTest {

	/**
	 * Runs the test suite in this class from the command line.
	 * 
	 * @param args	Arguments are ignored.
	 */
	public static void main(String[] args) {
		org.junit.runner.JUnitCore.runClasses(IntChannelTest.class);
	}
	
	/**
	 * Provides compatibility with 3.x versions of JUnit.
	 * 
	 * @return A 3.x-compatible test suite.
	 */
	public static junit.framework.Test suite() {
		return new JUnit4TestAdapter(IntChannelTest.class);
	}
	
	private static int DEFAULT_BUFFER_SIZE = 1024 * 32;

	private IntInputPort input;
	private IntOutputPort output;

    /*+----------------------------------------------------------------------+
	  |                                                                      | 
	  | Test the constructors.                                               | 
	  |                                                                      | 
	  +----------------------------------------------------------------------+*/
	
	@Test
	public void defaultConstructor() {
		IntChannel ch = new IntChannel();
		Assert.assertNotNull(ch);
	}
	
	@Test
	public void defaultInstanceClass() {
		IntChannel ch = new IntChannel();
		Assert.assertEquals(IntChannel.class, ch.getClass());
	}

	@Test
	public void constructorWithSize() {
		IntChannel ch = new IntChannel(DEFAULT_BUFFER_SIZE);
		Assert.assertNotNull(ch);
	}
	
	@Test
	public void instanceClassWithSize() {
		IntChannel ch = new IntChannel(DEFAULT_BUFFER_SIZE);
		Assert.assertEquals(IntChannel.class, ch.getClass());
	}
	
    /*+----------------------------------------------------------------------+
	  |                                                                      | 
	  | Test the write and read methods.                                     | 
	  |                                                                      | 
	  +----------------------------------------------------------------------+*/
	
	@Before
	public void setup() throws IOException {
		IntChannel channel = new IntChannel(DEFAULT_BUFFER_SIZE);			
		output = channel.getOutputPort();
		input = channel.getInputPort();
	}
	
	@After
	public void cleanup() throws IOException {
		input.close();
		output.close();
	}
	
	@Test
	public void receive() throws IOException {
		output.write(1);
		output.write(2);
		output.write(3);
		output.write(4);
		output.write(5);
		output.write(6);
		Assert.assertEquals(6, input.skipInts(6));
	}
	
	@Test
	public void receiveHighLoad() throws IOException {
		final int size = DEFAULT_BUFFER_SIZE - 1024;
		for (int i = 0; i < size; ++i) {
			output.write(9999);
		}
		Assert.assertEquals(size, input.skipInts(size));
	}
	
	@Test
	public void read() throws IOException {
		int[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
		output.write(data);
		for (int d : data)
			Assert.assertEquals(d, input.read());
	}
	
	@Test
	public void readArray() throws IOException {
		int[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
		output.write(data);
		int[] buffer = new int[data.length];
		input.read(buffer);
		for (int i = 0; i < data.length; ++i)
			Assert.assertEquals(data[i], buffer[i]);
	}
	
	@Test
	public void readSubArray() throws IOException {
		int[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
		int fill = 5005;
		output.write(data);

		int[] buffer = new int[data.length*2];
		Arrays.fill(buffer, fill);
		input.read(buffer, 10, 5);
		
		Assert.assertEquals(fill, buffer[9]);
		for (int i = 10; i < 15; ++i)
			Assert.assertEquals(data[i-10], buffer[i]);
		Assert.assertEquals(fill, buffer[15]);
	}
	
	@Test
	public void writeSubArray() throws IOException {
		int[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
		int fill = 5005;
		output.write(data, 3, 5);

		int[] buffer = new int[data.length*2];
		Arrays.fill(buffer, fill);
		input.read(buffer, 10, 5);
		
		Assert.assertEquals(fill, buffer[9]);
		for (int i = 10; i < 15; ++i)
			Assert.assertEquals(data[i-7], buffer[i]);
		Assert.assertEquals(fill, buffer[15]);
	}

	@Test
	public void skip() throws IOException {
		int size = 1024;
		int skip = 512;
		for (int i = 0; i < size; ++i) {
			output.write(199);
		}
		input.skipInts(skip);
		Assert.assertEquals(size-skip, input.skipInts(512));
	}
	
	@Test
	public void clear() throws IOException {
		int size = 1024;
		for (int i = 0; i < size; ++i) {
			output.write(33333333);
		}
		input.skipInts(input.skipInts(size));
		Assert.assertEquals(0, input.skipInts(1));
	}
	
	@Test(expected=EOFException.class)
	public void readTheEnd() throws IOException {
		int[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
		output.write(data);
		output.close();
		input.read(data);
		input.read(); // read one more than write: EOFException
	}

	
    /*+----------------------------------------------------------------------+
	  |                                                                      | 
	  | Negative tests for constructors.                                     | 
	  |                                                                      | 
	  +----------------------------------------------------------------------+*/
	
	@Test(expected=IllegalArgumentException.class)
	public void constructorWithZeroSize() throws IOException {
		new IntChannel(0);
	}
	
    /*+----------------------------------------------------------------------+
	  |                                                                      | 
	  | Negative tests for the write and read methods.                       | 
	  |                                                                      | 
	  +----------------------------------------------------------------------+*/

	@Test(expected=NullPointerException.class)
	public void writeNullArray() throws IOException {
		output.write(null);
	}
	
	@Test(expected=NullPointerException.class)
	public void readNullArray() throws IOException {
		input.read(null);
	}
	
	@Test(expected=IndexOutOfBoundsException.class)
	public void tryReadSubArray() throws IOException {
		int[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
		output.write(data);		
		int[] buffer = new int[data.length];
		input.read(buffer, 0, buffer.length+1); // off-by-one error
	}
	
	@Test(expected=IndexOutOfBoundsException.class)
	public void tryWriteSubArray() throws IOException {
		int[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
		output.write(data, 5, data.length);		
	}

}
